use syn::{Data, DataStruct, DeriveInput, Fields, FieldsNamed,
    GenericArgument, Path, PathArguments, Type, TypePath};
use proc_macro2::{Ident, TokenStream};
use darling::FromField;
use quote::quote;

#[derive(Debug, Default, FromField)]
#[darling(default, attributes(builder))]
struct Opts {
    each: Option<String>,
    default: Option<String>,
}

struct Fd {
    name: Ident,
    ty: Type,
    opts: Opts,
}

pub struct BuilderContext {
    name: Ident,
    fields: Vec<Fd>,
}

impl BuilderContext {
    pub fn new(input: DeriveInput) -> Self {
        let name = input.ident;
        let fields = if let Data::Struct(DataStruct {
            fields: Fields::Named(FieldsNamed {
                named, ..
            }), ..
        }) = input.data {
            named
        } else {
            panic!("Unsupported data type");
        };
        let fds = fields
            .into_iter()
            .map(|f| Fd {
                opts: Opts::from_field(&f).unwrap_or_default(),
                name: f.ident.unwrap(),
                ty: f.ty,
            })
            .collect();

        Self { name, fields: fds }
    }

    pub fn generate(self) -> TokenStream {
        let name = &self.name;

        // builder name: {}Builder e.g. CommandBuilder
        let builder_name = Ident::new(&format!("{}Builder", name), name.span());

        // optional fields: e.g. exec: String -> exec: Option<String>
        let optionized_fields = self.gen_optionized_fields();

        // methods: e.g. fn exec(mut self, v: impl Into<String>) -> Self { self.exec = Some(v); self }
        // CommandBuilder::builder().exec("ls").arg(vec![]).env(vec![]).finish()
        let methods = self.gen_methods();

        // assign builder fields back to original struct fields
        // #field_name: #self.#field_name.take().ok_or("missing field")
        let assigns = self.gen_assigns();

        quote! {
            /// Builder structure
            #[derive(Debug, Default)]
            struct #builder_name {
                // 字段展开
                #(#optionized_fields,)*
            }

            impl #builder_name {
                // 方法展开
                #(#methods)*

                pub fn finish(mut self) -> Result<#name, &'static str> {
                    Ok(#name {
                        #(#assigns,)*
                    })
                }
            }

            impl #name {
                pub fn builder() -> #builder_name {
                    Default::default()
                }
            }

        }
    }

    fn gen_optionized_fields<'a>(&'a self) -> impl Iterator<Item = TokenStream> + 'a {
        self.fields.iter().map(|f| {let name = &f.name;
            let (_, ty) = get_optional_inner(&f.ty);
            quote! { #name: std::option::Option<#ty> }
        })
    }

    fn gen_methods<'a>(&'a self) -> impl Iterator<Item = TokenStream> + 'a {
        self.fields.iter().map(|f| {
            let name = &f.name;
            let (_, ty) = get_optional_inner(&f.ty);
            let (is_vec, vec_inner_type) = get_vec_inner(&f.ty);

            if is_vec {
                if let Some(each_name) = f.opts.each.as_deref() {
                    let each_name = Ident::new(each_name, f.name.span());
                    return quote! {
                        pub fn #each_name(mut self, v: impl Into<#vec_inner_type>) -> Self {
                            let mut data = self.#name.take().unwrap_or_default();
                            data.push(v.into());
                            self.#name = Some(data);
                            self
                        }
                    };
                }
            }

            quote! {
                // e.g.
                // fn exec(mut self, v: impl Into<String>) -> Self {
                //     self.exec = Some(v);
                //     self
                // }
                pub fn #name(mut self, v: impl Into<#ty>) -> Self {
                    self.#name = Some(v.into());
                    self
                }
            }
        })
    }

    fn gen_assigns<'a>(&'a self) -> impl Iterator<Item = TokenStream> + 'a {
        self.fields.iter().map(|f| {
            let name = &f.name;
            let (optional, _) = get_optional_inner(&f.ty);
            if optional {
                return quote! { #name: self.#name.take() };
            }

            if let Some(default) = f.opts.default.as_deref() {
                let ast: TokenStream = default.parse().unwrap();
                return quote! { #name: self.#name.take().unwrap_or(#ast)};
            }

            quote! {
                // e.g.
                // #field_name: #self.#field_name.take().ok_or("missing field")
                // stringify! 宏，将标识符转换为字符串
                // concat! 宏，编译期连接字符串
                #name: self.#name.take().ok_or(concat!("missing field: ", stringify!(#name)))?
            }
        })
    }
}

fn get_optional_inner(ty: &Type) -> (bool, &Type) {
    get_type_inner(ty, "Option")
}

fn get_vec_inner(ty: &Type) -> (bool, &Type) {
    get_type_inner(ty, "Vec")
}


fn get_type_inner<'a>(ty: &'a Type, name: &str) -> (bool, &'a Type) {
    if let Type::Path(TypePath {
        path: Path {
            segments, ..
        }, ..
    }) = ty {
        if let Some(v) = segments.iter().next() {
            if v.ident == name {
                let t = match &v.arguments {
                    PathArguments::AngleBracketed(a) => match a.args.iter().next() {
                        Some(GenericArgument::Type(t)) => t,
                        _ => panic!("Not sure what to do with other GenericArgument"),
                    },
                    _ => panic!("Not sure what to do with other PathArguments"),
                };
                return (true, t);
            }
        }
    }
    (false, ty)
}
